上篇我們講了 parser 的基本使用方式和一些前置的 function 實作,像是 parseChannel
和 skip
,接下來我們就要來講其他實用的取值方式。首先,我們可以拿到指定 tag 的 value ,我們需要寫一個針對 XmlPullParser 來寫一個 readString 的 extension function 。
@Throws(IOException::class, XmlPullParserException::class)
protected fun XmlPullParser.readString(tagName: String): String? {
require(XmlPullParser.START_TAG, null, tagName)
var content: String? = null
if (next() == XmlPullParser.TEXT) {
content = text
nextTag()
if (eventType != XmlPullParser.END_TAG) {
skip()
nextTag()
content = null
}
}
require(XmlPullParser.END_TAG, null, tagName)
return content
}
我們可以取得指定 tagName
裡的值,假設 tag 的名稱叫做 title ,那代表的就是要拿 <title>title1</title>
裡面的 title1 。在 function 的最前面和最後面,我們都要檢查 event type 和現在的 tag 名稱,這主要是要檢查 RSS 的基本格式是不是正確的,因為有可能拿到的格式缺了前面的 <title>
或是後面的 </title>
,這都會導致我們後面的 parsing 結果不正確。在 function 的中間也要檢查 event type 是否為 TEXT
,這樣才是我們想要的值。
<enclosure length="24986239" type="audio/mpeg" url="http://item.enclosure.url/item.mp3" />
那如果我們是要拿 attribute 裡面的值,像是上面範例的 length
、 type
和 url
的話,可以怎麼做?其實他的思路跟 readString
有點像,但不需要額外爬一層 tag 。我們可以設計一個 function 讓他可以拿取多個 attribute !
@Throws(IOException::class, XmlPullParserException::class)
protected fun XmlPullParser.readAttributes(
tagName: String,
attributes: List<String>,
action: (String, String?) -> Unit
) {
require(XmlPullParser.START_TAG, null, tagName)
attributes.forEach { attr ->
action(attr, getAttributeValue(null, attr))
}
nextTag()
logD(logTag, "[readAttributes]: tag name = $tagName, attributes = $attributes")
require(XmlPullParser.END_TAG, null, tagName)
}
可以看到 function 的參數裡面有 attribute ,這個主要是讓使用者指定要爬的 attribute 有哪些,像剛剛的 enclosure 裡面我們就可以指定 tagName 是 enclosure ,attribute 是一個陣列,裡面包含 length
、 type
和 url
。 action
這個參數則是可以對爬到的值去做指定的動作,塞一個 lambda 進去就可以了!用法可以參考下面的程式碼:
@Throws(IOException::class, XmlPullParserException::class)
protected fun XmlPullParser.readEnclosure(): Enclosure {
var url: String? = null
var length: Long? = null
var type: String? = null
readAttributes(ENCLOSURE, listOf(URL, LENGTH, TYPE)) { attr, value ->
when (attr) {
URL -> url = value
LENGTH -> length = value?.toLongOrNull()
TYPE -> type = value
}
}
return Enclosure(url = url, length = length, type = type)
}